package mymyoux.graphics
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.utils.*;
	
	import mymyoux.events.LoadEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.graphics.controls.*;
	import mymyoux.graphics.graphicfactoryClasses.ConnectorEvent;
	import mymyoux.graphics.graphicfactoryClasses.EventConnector;
	import mymyoux.graphics.graphicfactoryClasses.Parser;
	import mymyoux.io.LoadAdvTextFile;
	import mymyoux.io.Parameters;
	import mymyoux.utils.Hash;
	import mymyoux.utils.Temporizer;
	/**
	 * Conteneur léger permettant la génération de tout élément graphique utilisant pour s'afficher les fonctions <b>addChild</b> ou <b>rawChildren.addChild</b>.
	 */
	public class GraphicFactory extends MEventDispatcher
	{
		/**
		 * Dispatched constant when an load is done.
		 */
		static public const DISPLAY_READY:String="display_ready"; 
		/**
		 * @private
		 */
		protected var _display:*;
	
		/**
		 * Contient la liste des éléments nommés.
		 */
		protected var conteneur:Hash;
		/**
		 * Contient la liste des évènements linkés.
		 */
		protected var events:Hash;
		/**
		 * Contient le parent où insérer la dernière interface chargée avec loadURL.
		 */
		 protected var parent:*;
		 /**
		 * Permet de charger le fichier XML grâce à loadURL.
		 */
		 protected var loader:LoadAdvTextFile;
		 /**
		 * An Hash of all used name. Used for copy.
		 */
		 protected var linkedName:Hash;
		 /**
		 * An Hash of all used parent. Used for copy.
		 */
		 protected var parents:Hash;
		 /**
		 * Used to temporarize the use of loadXML.
		 */
		 protected var temporizer:Temporizer;
		/**
		 * Crée un objet de type Conteneur Léger.
		 * @param display Element graphique de base.
		 */
		public function GraphicFactory(display:*)
		{
			
			_display=display

			 conteneur=new Hash();
			 events=new Hash();
			 temporizer=new Temporizer(_loadURL,this,1);
			linkedName=new  Hash();
			parents=new Hash();
			
		}
		/**
		 * Relie l'évènement <b><i>name</i></b> à la fonction indiquée.
		 * @param name Nom de l'évènement.
		 * @param fonction Fonction à connectée.
		 */
		public function connect(name:String,fonction:Function):void
		{
			if(!events.containsKey(name))
			{
				events.put(name,new Array());
			}
			(events.get(name) as Array).push(fonction);
		}
		/**
		 * Délie l'évènement <b><i>name</i></b> à la fonction indiquée.
		 * @param name Nom de l'évènement.
		 * @param fonction Fonction à déconnectée.
		 */
		public function disconnect(name:String,fonction:Function):void
		{
			var p:String;
			var keys:Array;
			var nkeys:Array=new Array();
			if(events.containsKey(name))
			{
				keys=events.get(name) as Array;
				for(p in keys)
				{
					if(keys[p]===fonction)
					{
						
					}else
					{
						nkeys.push(keys[p]);
					}
					
				}
				events.put(name,nkeys);
			}
		}
		/**
		 * Indique si le conteneur léger contient l'enfant indiquée par son nom.
		 * @param name Nom de l'enfant à tester.
		 * @return <b>true</b> si l'enfant existe, <b>false</b> sinon.
		 */
		public function hasChild(name:String):Boolean
		{
			return conteneur.containsKey(name);
		}
		/**
		 * Renvoie l'enfant indiqué par le nom.
		 * @param name Nom de l'enfant à retourner.
		 * @return L'enfant s'il existe, <b>new Object()</b> sinon.
		 */
		public function getChild(name:String):*
		{
			if(!hasChild(name))
			{
				trace("Warning : objet ["+name+"] n'existe pas!");
				return null;
			}
			return conteneur.get(name);
		}
		/**
		 * Ajoute un évènement à linker par le conteneur léger.
		 * @param displayobject Object ou nom de l'Object dont l'évènement est à linké.
		 * @param name Nom du lien.
		 * @param value Type de l'évènement.
		 */
		public function addEvent(displayobject:*,name:String,value:String):void
		{
			var connector:EventConnector=new EventConnector(name,displayobject is String?getChild(displayobject):displayobject,value);
			connector.addEventListener(ConnectorEvent.CONNECTOR,onEvent);
		}
		/**
		 * Appelée lorsqu'un évenement linké est dispatché.
		 * @param event ConnectorEvent correspondant à l'évènement.
		 */
		protected function onEvent(event:ConnectorEvent):void
		{
			var p:String;
			var keys:Array;
			if(events.containsKey(event.name))
			{
				keys=events.get(event.name) as Array;
				for(p in keys)
				{
					(keys[p] as Function).apply(event.object,new Array(event.value));				
				}
			}
		}
		/**
		 * Duplicate an named item. It works only for automatic named item.
		 * @param name Name of the item to duplicate.
		 * @param newname Name of the new item.
		 * @param newparent Name of the new parent.
		 */
		public function copy(name:String,newname:String=null,newparent:*=null):void
		{
			if(newname==null)
			{
				var i:int=0;
				while(hasChild(name+i))
				{
					i++;
				}
				newname=name+i;
			}
			
			if(linkedName.containsKey(name))
			{
				var xml:XML=linkedName.get(name) as XML;
				xml.@name=newname;
				this.load(new XML("<root>"+xml.toXMLString()+"</root>"),parents.get(name) as DisplayObject);
			}else
			{
				
				trace("Warning : impossible copy, no symbol found");
			}
		}
		/**
		 * Ajoute un enfant dans le conteneur léger.
		 * @param displayobject Enfant à rajouter.
		 * @param name Nom de l'enfant.
		 * @param parent Parent ou nom du parent.
		 */
		public function addChild(displayobject:*,name:String,parent:*,xlist:XML):void
		{
			if(name.length>0)
			{
				if(conteneur.containsKey(name))
				{
					trace("Warning : objet déjà dans le hash");
					var i:int=0;
					while(conteneur.containsKey(name+i))
					{
						i++;
					}
					name=name+i;
				}
				//on enregistre le xml dans la linkedName. Utile pour la copy
				linkedName.put(name,xlist);
				parents.put(name,parent);
				conteneur.put(name,displayobject);
			}
			if(parent==null || (parent is String && !hasChild(parent)))
			{
				parent=_display;
			}else
			{
				if(parent is String)
				{
					parent=getChild(parent);	
				}
			}
			try
			{
				displayobject.name=name;
				parent.addChild(displayobject);
			}catch(error:Error)
			{
				//MXML
				parent.rawChildren.addChild(displayobject);
			}
			
		}
		/**
		 * Vide le displayobject en supprimant ses descendants du conteneur léger.
		 * @param displayobject Object ou nom de l'Objec à vider.
		 */
		public function clearChild(displayobject:*):void
		{
			var objet:*;
			if(displayobject is String)
			{
				objet=getChild(displayobject);
			}else
			{
				objet=displayobject;
			}
			if(objet!=null)
			{
				
				try
				{	
					while(objet.getChildren().length>0)
					{
						removeChild(objet.getChildren()[0]);
					}
				}catch(error:Error)
				{
					
					try
					{
						//douteux
						if(error.toString().indexOf(" mx.co")==-1)
						{
							while(objet.numChildren>0)
							{
								removeChild(objet.getChildAt(0));
							}
						}else
						{
							//trace(error);
						}
					
					}catch(err:Error)
					{
						//trace(err);
					}
				}
			}
		}
		/**
		 * Supprime le displayobject ainsi que ses descendants du conteneur léger.
		 * @param displayobject Object ou nom de l'Objec à supprimer.
		 */
		public function removeChild(displayobject:*):void
		{
			var objet:*;
			if(displayobject is String)
			{
				objet=getChild(displayobject);
			}else
			{
				objet=displayobject;
			}
			if(objet!=null)
			{
				
				try
				{	
					while(objet.getChildren().length>0)
					{
						removeChild(objet.getChildren()[0]);
					}
				}catch(error:Error)
				{
					
					try
					{
						//douteux
						if(error.toString().indexOf(" mx.co")==-1)
						{
							while(objet.numChildren>0)
							{
								removeChild(objet.getChildAt(0));
							}
						}else
						{
							//trace(error);
						}
					
					}catch(err:Error)
					{
						//trace(err);
					}
				}
				try
				{
					objet.parent.removeChild(objet);
				}catch(error:Error)
				{
					
				}
				
				//supression de la référence
				if(conteneur.get(objet.name)===objet)
				{
					conteneur.remove(objet.name);
				}
			}
		}
		/**
		 * Retourne l'élément graphique du conteneur léger.
		 * @return L'élément graphique.
		 */
		public function get display():*
		{
			return _display;
		}
		/**
		 * Appelée lorsque le parser a terminé.
		 * @parame event Event simple indiquant la fin du parsage.
		 */
        protected function parserComplete(event:Event):void
        {
           dispatchEvent(new Event(DISPLAY_READY));
        }
        /**
        * Permet de charger un fichier XML à parser pour le conteneur léger.
        * @param url Url à charger.
        * @param parent Parent dans le conteneur sur lequel charger la nouvelle interface.
        * @param args Arguments à transmettre à l'Url.
        */
        public function loadURL(url:String,parent:*=null,args:Parameters=null):void
        {
        	temporizer.add(url,parent,args);
        } 
        /**
        * @private
        */
        protected function _loadURL(url:String,parent:*=null,args:Parameters=null):void
        {
        	var loader:LoadAdvTextFile=new LoadAdvTextFile(url,args);
        	this.parent=parent;
        	loader.addEventListener(LoadAdvTextFile.COMPLETED,onLoadedURL);
        }
        protected function onLoadedURL(event:LoadEvent):void
        {
        	temporizer.release();
        	try
        	{
        		var xml:XML=new XML(event.value);
        		trace(xml.toXMLString()+" "+parent);
        		load(xml,parent);
        	}catch(error:Error)
        	{
        		trace("Error de Conteneur léger : ["+event.url+"] n'est pas valide");
        	}
        }
		/**
		 * Permet de parser un fichier XML par le conteneur léger.
		 * @param screen XML Contenant l'interface à charger.
		 * @param parent Parent dans le conteneur sur lequel charger la nouvelle interface.
		 */
		public function load(screen:XML,parent:*=null):void
		{
			var name:String;
			
			try
			{
				name=parent is String?parent:parent.name;
				if(hasChild(name))
				{
					parent=getChild(name);
				}else
				{
					//parent=null;
				}
			}catch(error:Error)
			{
				parent=null;
				trace("Le parent doit être soit un objet du conteneur soit son nom - parent est null maintenant");
			}
			
			new Parser(this,screen,parent).addEventListener(Parser.END,parserComplete);

		}

	}
}